#ifndef RDMA_COMMON_H
#define RDMA_COMMON_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>

#include <netdb.h>
#include <netinet/in.h>	
#include <arpa/inet.h>
#include <sys/socket.h>

#include <rdma/rdma_cma.h>
#include <infiniband/verbs.h>


// 只获取文件名, 过滤掉文件路径
#define __SFILE__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)


//! 日志调试宏
// ERROR
#define rdma_error(message, ...) fprintf(stderr, "[ERROR] (%s:%d:%s) " message "\n",\
                __SFILE__, __LINE__, __func__, ##__VA_ARGS__)

// WARN
#define rdma_warn(message, ...) fprintf(stderr,  "[WARN] (%s:%d:%s) "  message "\n",\
                __SFILE__, __LINE__, __func__, ##__VA_ARGS__)

// INFO
#define rdma_info(message, ...) fprintf(stderr,  "[INFO] (%s:%d:%s) "  message "\n",\
                __SFILE__, __LINE__, __func__, ##__VA_ARGS__)

// DEBUG
#define rdma_debug(message, ...) fprintf(stderr, "[DEBUG] (%s:%d:%s) " message "\n",\
                __SFILE__, __LINE__, __func__, ##__VA_ARGS__)


// #define rdma_debug(message, ...)


//! 定义一些超参数
#define CQ_CAPACITY (16)    // 完成队列的荣阿玲
#define MAX_SGE     (2)     // SGE的大小
#define MAX_WR      (8)     // Worker Request的最大数量
#define RDMA_PORT   (8899)  // RDMA Server端端口


//! Client端与Server交换的元数据信息
// 双方相互交换自己用于开放RDMA读写的内存区域信息
struct __attribute((packed)) meta_attr {
	uint64_t address;      // 内存地址
	uint32_t length;       // 内存空间大小

	union key {
		uint32_t lkey;     // 本地key
		uint32_t rkey;     // 远程key
	} key;
};



//! 根据char* dst 获取对应的sin_addr
int get_addr(char *dst, struct sockaddr *addr);




//! 打印内存元数据信息
void show_meta(struct meta_attr *attr);




/**
 * @brief 从CM事件管道中获取CM事件
 * 
 * @param channel        CM事件管道,用于通知CM事件
 * @param expected_event 希望接收的CM事件
 * @param cm_event       用于存放真实获取到的CM时间
 * @return int 
 */
int cm_event_callback(struct rdma_event_channel *channel, 
					enum rdma_cm_event_type expected_event,
					struct rdma_cm_event **cm_event);




/**
 * @brief 分配内存并将其注册为MR
 * 
 * @param pd         分配的保护区域，将注册MR位于该PD下
 * @param size       内存区域大小
 * @param permission 该MR的访问权限
 * @return struct ibv_mr* 返回分配好的MR
 */
struct ibv_mr* mr_alloc(struct ibv_pd *pd, uint32_t size, 
						enum ibv_access_flags permission);




/**
 * @brief 取消注册MR并释放其拥有的内存
 * 
 * @param mr  MR指针
 */
void mr_free(struct ibv_mr *mr);



/**
 * @brief 将内存地址注册为DRMA设备可(安全)访问的MR
 * 
 * @param pd          保护域PD, MR绑定到该PD
 * @param addr        内存地址
 * @param length      内存大小
 * @param permission  内存的访问权限
 * @return struct ibv_mr* 
 */
struct ibv_mr *mr_register(struct ibv_pd *pd, void *addr, 
		uint32_t length, enum ibv_access_flags permission);




/**
 * @brief 反注册MR
 * 
 * @param mr MR指针
 */
void mr_deregister(struct ibv_mr *mr);




/**
 * @brief 阻塞等待处理WC事件管道的WC事件
 * 
 * @param channel  WC事件管道	
 * @param wc       WC指针
 * @param max_wc   最大WC数量
 * @return int 
 */
int wc_event_callback(struct ibv_comp_channel *channel, 
					struct ibv_wc *wc, int max_wc);




/**
 * @brief 打印CM事件的详细信息
 * 
 * @param id 
 */
void log_cm_id(struct rdma_cm_id *id);


#endif /* RDMA_COMMON_H */